package com.joker.demo.core.executor.impl;

import com.joker.demo.core.annotation.JobHandle;
import com.joker.demo.core.config.JobExecutorProperties;
import com.joker.demo.core.domain.bo.JobRegistryDetailBO;
import com.joker.demo.core.domain.dto.JobExecutorParamsDTO;
import com.joker.demo.core.domain.dto.JobExecutorResultDTO;
import com.joker.demo.core.domain.dto.TaskExecuteRequestDTO;
import com.joker.demo.core.domain.enums.JobTimeUnitEnum;
import com.joker.demo.core.exception.BizException;
import com.joker.demo.core.executor.JobExecutor;
import com.joker.demo.core.executor.JobExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 任务执行器 - Spring Boot版本
 * <p>
 * 这个类专门为Spring Boot项目设计，提供以下功能：
 * 1. 自动扫描Spring容器中的任务处理器
 * 2. 支持类级别和方法级别的@JobHandle注解
 * 3. 与Spring生命周期集成，自动启动和销毁
 * 4. 提供手动注册任务处理器的方法
 * <p>
 * 实现的关键接口：
 * - ApplicationContextAware: 获取Spring应用上下文
 * - SmartInitializingSingleton: 在所有单例Bean初始化完成后启动
 * - DisposableBean: 在Spring容器销毁时清理资源
 * <p>
 * 使用方式：
 * 1. 在Spring Boot项目中配置此Bean
 * 2. 在类或方法上添加@JobHandle注解
 * 3. Spring会自动扫描并注册任务处理器
 * <p>
 * 特殊标识说明：
 * - 由于ConcurrentHashMap不允许null值，使用NULL_METHOD特殊标识来标注类级别的任务处理器
 * - 使用HandlerType枚举来标识任务处理器的类型（类级别/方法级别）
 * - 使用jobHandlerTypeMap来存储任务名称到处理器类型的映射，用于重复检查和日志记录
 *
 * @author : feixiang.li
 * @since : 2025-07-24 13:46
 */
public class JobExecutorSpringbootExecutor extends JobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {

    /**
     * 日志记录器
     */
    private static final Logger log = LoggerFactory.getLogger(JobExecutorSpringbootExecutor.class);

    /**
     * Spring应用上下文
     */
    private ApplicationContext applicationContext;

    /**
     * 存储任务名称到处理方法的映射
     * 使用特殊标识来避免null值：
     * - 方法级别的任务处理器：存储实际的Method对象
     * - 类级别的任务处理器：存储NULL_METHOD标识
     */
    private final Map<String, Method> jobHandlerMap = new ConcurrentHashMap<>();

    /**
     * 存储任务名称到处理器实例的映射
     * 使用特殊标识来避免null值：
     * - 方法级别的任务处理器：存储实际的实例对象
     * - 类级别的任务处理器：存储实际的实例对象
     */
    private final Map<String, Object> jobHandlerInstanceMap = new ConcurrentHashMap<>();

    /**
     * 特殊标识：用于标注类级别的任务处理器
     * 因为ConcurrentHashMap不允许null值，所以使用这个特殊标识
     */
    private final Method NULL_METHOD;

    /**
     * 特殊标识：用于标注任务处理器类型
     */
    private static final String CLASS_LEVEL_HANDLER = "CLASS_LEVEL";
    private static final String METHOD_LEVEL_HANDLER = "METHOD_LEVEL";

    /**
     * 任务处理器类型枚举
     */
    private enum HandlerType {
        CLASS_LEVEL("类级别"), METHOD_LEVEL("方法级别");

        private final String description;

        HandlerType(String description) {
            this.description = description;
        }

        public String getDescription() {
            return description;
        }
    }

    /**
     * 存储任务名称到处理器类型的映射
     * 用于重复检查和日志记录
     */
    private final Map<String, HandlerType> jobHandlerTypeMap = new ConcurrentHashMap<>();

    /**
     * 存储任务名称到JobHandle的映射
     */
    private final Map<String, JobHandle> jobHandleMap = new ConcurrentHashMap<>();

    /**
     * 构造函数
     *
     * @param jobExecutorProperties 任务执行器配置
     */
    public JobExecutorSpringbootExecutor(JobExecutorProperties jobExecutorProperties) {
        super(jobExecutorProperties);
        try {
            Class<?> aClass = Class.forName("com.joker.demo.core.executor.impl.JobExecutorNullMethod");
            NULL_METHOD = aClass.getMethods()[0];
        } catch (ClassNotFoundException e) {
            throw new BizException(e);
        }
    }

    /**
     * Spring容器销毁时的清理方法
     * 实现DisposableBean接口，确保在Spring容器销毁时正确清理资源
     */
    @Override
    public void destroy() throws Exception {
        log.info("Spring容器销毁，开始清理任务执行器资源");
        stop();
    }

    /**
     * 在所有单例Bean初始化完成后执行
     * 实现SmartInitializingSingleton接口，确保在所有Bean都初始化完成后再启动任务执行器
     * 这样可以确保所有任务处理器都已经注册到Spring容器中
     */
    @Override
    public void afterSingletonsInstantiated() {
        log.info("所有单例Bean初始化完成，开始启动任务执行器");

        // 第一步：扫描Spring容器中的任务处理器
        scanJobHandlers();

        // 第二步：启动任务执行器，连接到调度中心并注册任务
        startSpringBoot();
    }

    /**
     * 设置Spring应用上下文
     * <p>
     * 实现ApplicationContextAware接口，Spring会自动注入应用上下文
     *
     * @param applicationContext Spring应用上下文
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 扫描Spring容器中带有@JobHandle注解的Bean
     * <p>
     * 扫描过程：
     * 1. 获取Spring容器中的所有Bean
     * 2. 检查每个Bean的类和方法上的@JobHandle注解
     * 3. 将找到的任务处理器注册到映射表中
     * <p>
     * 支持三种类型的任务处理器：
     * 1. 方法级别 - 无参数：在方法上添加@JobHandle注解，方法无参数
     * 2. 方法级别 - 有参数：在方法上添加@JobHandle注解，方法格式和JobExecutorService接口一样
     * 3. 类级别：在类上添加@JobHandle注解，必须实现JobExecutorService接口
     * <p>
     * 重要规则：
     * - 支持分片参数的任务处理器必须实现JobExecutorService接口或方法格式和接口一样
     * - 所有jobName不能重复，重复时会报错
     */
    public void scanJobHandlers() {
        log.info("开始扫描Spring容器中的任务处理器");

        try {
            // 获取Spring容器中的所有Bean名称
            String[] beanNames = applicationContext.getBeanDefinitionNames();

            // 用于检查jobName重复的临时集合
            Map<String, String> jobNameToHandler = new ConcurrentHashMap<>();

            // 遍历所有Bean
            for (String beanName : beanNames) {
                try {
                    // 获取Bean实例
                    Object bean = applicationContext.getBean(beanName);
                    Class<?> beanClass = bean.getClass();

                    // 检查类上是否有@JobHandle注解
                    JobHandle classAnnotation = beanClass.getAnnotation(JobHandle.class);
                    if (classAnnotation != null) {
                        String jobName = classAnnotation.value();

                        // 检查jobName是否重复
                        if (jobNameToHandler.containsKey(jobName)) {
                            HandlerType existingType = jobHandlerTypeMap.get(jobName);
                            String existingTypeDesc = existingType != null ? existingType.getDescription() : "未知";
                            throw new BizException(String.format("任务名称重复: %s, 已存在处理器: %s, 当前处理器: %s (类级别)", jobName, existingTypeDesc, beanClass.getName()));
                        }

                        // 类级别的任务处理器必须实现JobExecutorService接口
                        if (JobExecutorService.class.isAssignableFrom(beanClass)) {
                            // 注册类级别的任务处理器
                            // 使用特殊标识
                            jobHandlerMap.put(jobName, NULL_METHOD);
                            jobHandlerInstanceMap.put(jobName, bean);
                            jobHandlerTypeMap.put(jobName, HandlerType.CLASS_LEVEL);
                            jobNameToHandler.put(jobName, CLASS_LEVEL_HANDLER);
                            jobHandleMap.put(jobName, classAnnotation);
                            log.info("注册任务处理器类: {} -> {} (支持分片参数)", jobName, beanClass.getName());
                        } else {
                            throw new BizException(String.format("类级别的任务处理器必须实现JobExecutorService接口: %s", beanClass.getName()));
                        }
                    }

                    // 扫描方法上的@JobHandle注解
                    Method[] methods = beanClass.getMethods();
                    for (Method method : methods) {
                        JobHandle methodAnnotation = method.getAnnotation(JobHandle.class);
                        if (methodAnnotation != null) {
                            String jobName = methodAnnotation.value();

                            // 检查jobName是否重复
                            if (jobNameToHandler.containsKey(jobName)) {
                                HandlerType existingType = jobHandlerTypeMap.get(jobName);
                                String existingTypeDesc = existingType != null ? existingType.getDescription() : "未知";
                                throw new BizException(String.format("任务名称重复: %s, 已存在处理器: %s, 当前处理器: %s.%s (方法级别)", jobName, existingTypeDesc, beanClass.getName(), method.getName()));
                            }

                            // 检查方法参数类型
                            Class<?>[] parameterTypes = method.getParameterTypes();
                            boolean hasParams = parameterTypes.length > 0;

                            // 检查方法格式是否和JobExecutorService接口一样
                            boolean isInterfaceFormat = hasParams && parameterTypes.length == 1 && parameterTypes[0] == JobExecutorParamsDTO.class && method.getReturnType() == JobExecutorResultDTO.class;

                            // 如果方法有参数，必须实现JobExecutorService接口或方法格式和接口一样
                            if (hasParams && !isInterfaceFormat) {
                                if (!JobExecutorService.class.isAssignableFrom(beanClass)) {
                                    throw new BizException(String.format("支持分片参数的任务处理器必须实现JobExecutorService接口或方法格式和接口一样: %s.%s", beanClass.getName(), method.getName()));
                                }
                            }

                            // 注册方法级别的任务处理器
                            jobHandlerMap.put(jobName, method);
                            jobHandlerInstanceMap.put(jobName, bean);
                            jobHandlerTypeMap.put(jobName, HandlerType.METHOD_LEVEL);
                            jobNameToHandler.put(jobName, METHOD_LEVEL_HANDLER);
                            jobHandleMap.put(jobName, methodAnnotation);

                            if (hasParams) {
                                if (isInterfaceFormat) {
                                    log.info("注册任务处理器方法: {} -> {}.{} (有参数，格式和接口一样)", jobName, beanClass.getName(), method.getName());
                                } else {
                                    log.info("注册任务处理器方法: {} -> {}.{} (有参数，通过接口实现)", jobName, beanClass.getName(), method.getName());
                                }
                            } else {
                                log.info("注册任务处理器方法: {} -> {}.{} (无参数)", jobName, beanClass.getName(), method.getName());
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("扫描Bean {} 时发生异常", beanName, e);
                    // 重新抛出异常，确保扫描失败时能够及时发现
                    throw new BizException("任务处理器扫描失败", e);
                }
            }
            log.info("Spring容器中的任务处理器扫描完成，共注册 {} 个处理器", jobHandlerMap.size());
        } catch (Exception e) {
            log.error("任务处理器扫描时发生异常", e);
            throw new BizException("任务处理器扫描失败", e);
        }
    }

    /**
     * 获取要注册到调度中心的任务处理器列表
     * <p>
     * 遍历所有已注册的任务处理器，构建注册信息
     * <p>
     * 分片支持判断逻辑：
     * 1. 类级别的任务处理器：默认支持分片（实现JobExecutorService接口）
     * 2. 方法级别的任务处理器：
     * - 无参数：不支持分片
     * - 有参数，格式和接口一样：支持分片
     * - 有参数，通过接口实现：支持分片
     *
     * @return 任务注册信息列表
     */
    @Override
    public List<JobRegistryDetailBO> getRegisterJobHandlers() {
        log.info("开始构建任务处理器注册信息");

        List<JobRegistryDetailBO> jobRegistryList = new ArrayList<>();

        // 遍历所有已注册的任务处理器
        for (String jobName : jobHandlerMap.keySet()) {
            try {
                JobRegistryDetailBO registry = new JobRegistryDetailBO();
                registry.setJobName(jobName);
                JobHandle classAnnotation = jobHandleMap.get(jobName);
                registry.setJobDesc(classAnnotation.desc());
                registry.setCronExpression(classAnnotation.cron());
                registry.setRetryCount(classAnnotation.retryCount());
                registry.setTimeout(classAnnotation.timeout());
                registry.setTimeoutUnit(JobTimeUnitEnum.convertToTimeUnit(classAnnotation.timeoutUnit()));
                registry.setAlarmEmail(classAnnotation.alarmEmail());
                // 判断是否支持分片参数
                Method method = jobHandlerMap.get(jobName);
                Object instance = jobHandlerInstanceMap.get(jobName);

                if (method != NULL_METHOD) {
                    // 方法级别的任务处理器
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    boolean hasParams = parameterTypes.length > 0;

                    // 检查方法格式是否和JobExecutorService接口一样
                    boolean isInterfaceFormat = hasParams && parameterTypes.length == 1 && parameterTypes[0] == JobExecutorParamsDTO.class && method.getReturnType() == JobExecutorResultDTO.class;

                    if (hasParams) {
                        // 有参数，支持分片
                        registry.setSharding(1);
                        if (isInterfaceFormat) {
                            log.debug("任务处理器 {} 支持分片参数 (方法格式和接口一样)", jobName);
                        } else {
                            log.debug("任务处理器 {} 支持分片参数 (通过接口实现)", jobName);
                        }
                    } else {
                        // 无参数，不支持分片
                        registry.setSharding(0);
                        log.debug("任务处理器 {} 不支持分片参数", jobName);
                    }
                } else if (instance != null) {
                    // 类级别的任务处理器，默认支持分片
                    registry.setSharding(1);
                    log.debug("任务处理器 {} (类级别) 支持分片参数", jobName);
                } else {
                    // 未知类型，默认不支持分片
                    registry.setSharding(0);
                    log.warn("任务处理器 {} 类型未知，默认不支持分片", jobName);
                }
                log.info("任务处理器 {} 注册信息：{}", jobName, registry);
                jobRegistryList.add(registry);
            } catch (Exception e) {
                log.error("注册任务处理器 {} 时发生异常", jobName, e);
            }
        }

        log.info("任务处理器注册信息构建完成，共注册 {} 个处理器", jobRegistryList.size());
        return jobRegistryList;
    }

    /**
     * 执行具体的任务
     * <p>
     * 根据任务名称找到对应的处理器，然后执行任务
     * <p>
     * 执行逻辑：
     * 1. 方法级别的任务处理器：
     * - 无参数：直接调用方法
     * - 有参数，格式和接口一样：直接调用方法，传入JobExecutorParamsDTO参数
     * - 有参数，通过接口实现：通过类的execute方法处理
     * 2. 类级别的任务处理器：调用execute方法，传入JobExecutorParamsDTO参数
     *
     * @param request 任务参数
     * @return 执行结果
     */
    @Override
    public JobExecutorResultDTO executeTask(TaskExecuteRequestDTO request) {
        String jobName = request.getJobName();
        JobExecutorParamsDTO params = new JobExecutorParamsDTO();
        params.setShardingParam(request.getShardingParam());
        params.setShardingParamIndex(request.getShardingParamIndex());
        params.setPrevTaskResult(request.getPrevTaskResult());

        log.error("开始执行任务: {}, 参数: {}", jobName, params);

        try {
            // 获取任务处理器信息
            Method method = jobHandlerMap.get(jobName);
            Object instance = jobHandlerInstanceMap.get(jobName);

            if (method != NULL_METHOD && instance != null) {
                // 方法级别的任务处理器
                Object result;
                Class<?>[] parameterTypes = method.getParameterTypes();
                boolean hasParams = parameterTypes.length > 0;

                // 检查方法格式是否和JobExecutorService接口一样
                boolean isInterfaceFormat = hasParams && parameterTypes.length == 1 && parameterTypes[0] == JobExecutorParamsDTO.class && method.getReturnType() == JobExecutorResultDTO.class;

                if (hasParams) {
                    if (isInterfaceFormat) {
                        // 方法格式和接口一样，直接调用方法
                        result = method.invoke(instance, params);
                        log.info("调用方法级别的任务处理器: {}.{} (有参数，格式和接口一样)", instance.getClass().getName(), method.getName());
                    } else {
                        // 通过类的execute方法处理
                        if (instance instanceof JobExecutorService) {
                            JobExecutorService service = (JobExecutorService) instance;
                            result = service.execute(params);
                            log.info("调用方法级别的任务处理器: {}.{} (有参数，通过接口实现)", instance.getClass().getName(), method.getName());
                        } else {
                            throw new RuntimeException("任务处理器必须实现JobExecutorService接口");
                        }
                    }
                } else {
                    // 无参数，直接调用方法
                    result = method.invoke(instance);
                    log.info("调用方法级别的任务处理器: {}.{} (无参数)", instance.getClass().getName(), method.getName());
                }

                // 构建执行结果
                if (result instanceof JobExecutorResultDTO) {
                    return (JobExecutorResultDTO) result;
                } else {
                    JobExecutorResultDTO executorResult = new JobExecutorResultDTO();
                    executorResult.setResult(String.valueOf(result));
                    return executorResult;
                }

            } else if (instance instanceof JobExecutorService) {
                // 类级别的任务处理器
                JobExecutorService service = (JobExecutorService) instance;
                log.info("调用类级别的任务处理器: {}", instance.getClass().getName());
                return service.execute(params);

            } else {
                log.error("未找到任务处理器: {}", jobName);
                throw new RuntimeException("未找到任务处理器: " + jobName);
            }

        } catch (Exception e) {
            log.error("执行任务失败: {}", jobName, e);
            // 构建错误结果
            JobExecutorResultDTO errorResult = new JobExecutorResultDTO();
            errorResult.setResult("执行失败: " + e.getMessage());
            return errorResult;
        }
    }

    /**
     * 手动注册任务处理器（类级别）
     * <p>
     * 用于手动注册任务处理器，不依赖自动扫描
     *
     * @param jobName 任务名称
     * @param handler 任务处理器实例
     */
    public void registerJobHandler(String jobName, Object handler) {
        if (handler instanceof JobExecutorService) {
            jobHandlerMap.put(jobName, NULL_METHOD); // 使用特殊标识
            jobHandlerInstanceMap.put(jobName, handler);
            jobHandlerTypeMap.put(jobName, HandlerType.CLASS_LEVEL);
            log.info("手动注册任务处理器类: {} -> {}", jobName, handler.getClass().getName());
        } else {
            log.error("任务处理器必须实现JobExecutorService接口: {}", jobName);
        }
    }

    /**
     * 手动注册任务处理器方法
     * <p>
     * 用于手动注册任务处理器方法，不依赖自动扫描
     *
     * @param jobName  任务名称
     * @param instance 处理器实例
     * @param method   处理方法
     */
    public void registerJobHandlerMethod(String jobName, Object instance, Method method) {
        jobHandlerMap.put(jobName, method);
        jobHandlerInstanceMap.put(jobName, instance);
        jobHandlerTypeMap.put(jobName, HandlerType.METHOD_LEVEL);
        log.info("手动注册任务处理器方法: {} -> {}.{}", jobName, instance.getClass().getName(), method.getName());
    }
}
